// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module A_cse_ocs_dpa_aes10c_gf16_mul #(
      parameter POLY1 = 3 
   )
   (
      input  logic [3:0] a,
      input  logic [3:0] b,
      output logic [3:0] o
   );
   
   // internal signals
   wire [3:0]   x;
   
   // continuous assignments (combinational logic)
   generate
     if (POLY1 == 3)  begin
       assign       x = {a[3],a[3]} ^ {a[2],a[0]};
       assign       o[0] = ~(a[0] & b[0]) ^ ~(a[3] & b[1]) ^  ~(a[2] & b[2]) ^ ~(a[1] & b[3]);
       assign       o[1] = ~(a[1] & b[0]) ^ ~(x[0] & b[1]) ^  ~(x[1] & b[2]) ^ ~((a[2] ^ a[1]) & b[3]);
       assign       o[2] = ~(a[2] & b[0]) ^ ~(a[1] & b[1]) ^  ~(x[0] & b[2]) ^ ~(x[1] & b[3]);
       assign       o[3] = ~(a[3] & b[0]) ^ ~(a[2] & b[1]) ^  ~(x[0] & b[3]) ^ ~(a[1] & b[2]);
     end
     if (POLY1 == 9)  begin
       assign       x[0] = a[3] ^ a[2];
       assign       o[0] = ~(a[0] & b[0]) ^ ~(a[3] & b[1]) ^  ~(x[0] & b[2]) ^ ~((x[0] ^ a[1]) & b[3]);
       assign       o[1] = ~(a[1] & b[0]) ^ ~(a[0] & b[1]) ^  ~(a[3] & b[2]) ^ ~(x[0] & b[3]);
       assign       o[2] = ~(a[2] & b[0]) ^ ~(a[1] & b[1]) ^  ~(a[0] & b[2]) ^ ~(a[3] & b[3]);
       assign       o[3] = ~(a[3] & b[0]) ^ ~(x[0] & b[1]) ^  ~((x[0]^a[1]) & b[2]) ^ ~((x[0]^a[1]^a[0]) & b[3]);
     end
     if (POLY1 == 15)  begin
       assign       x[0] = a[3] ^ a[2];
       assign       x[1] = a[3] ^ a[1];
       assign       x[2] = a[2] ^ a[1];
       assign       o[0] = ~(a[0] & b[0]) ^ ~(a[3] & b[1]) ^  ~(x[0] & b[2]) ^ ~(x[2] & b[3]);
       assign       o[1] = ~(a[1] & b[0]) ^ ~((a[0] ^ a[3]) & b[1]) ^  ~(a[2] & b[2]) ^ ~(x[1] & b[3]);
       assign       o[2] = ~(a[2] & b[0]) ^ ~(x[1] & b[1]) ^  ~((a[0] ^ a[2]) & b[2]) ^ ~(a[1] & b[3]);
       assign       o[3] = ~(a[3] & b[0]) ^ ~(x[0] & b[1]) ^  ~(x[2] & b[2]) ^ ~((a[1] ^ a[0]) & b[3]);
     end
   endgenerate
endmodule

